C++很多强大的语言特性导致它的复杂,其复杂性会使得代码更容易出现bug、难于阅读和维护。 由于,本人有一点点代码洁癖,所以依照Google的C++编程规范《Google C++ Style Guide》,用来约束自己平时编程,使得代码在有效使用C++语言特性的同时易于管理。 |
|||
分类 |
标题 |
规则 |
备注(示例) |
头文件 每个.cpp文件都应对应一个.h(.hpp)文件 |
#define保护 |
1.#define PROJECT_PATH_FILE_H_防止.h文件被多重包含; |
Project sdk中.h文件sdk/src/abc.h,#define如下: 1 #ifndef SDK_SRC_ABC_H_ 2 #define SDK_SRC_ABC_H_ 3 ... 4 #endif // SDK_SRC_ABC_H_ |
前置声明 |
1. 使用前置声明尽量减少文件中#include的数量; 2. 使用函数时,采用#include方式; 3. 使用类模版时,采用#include方式; 4. 使用普通类时,采用前置声明; 5. 数据成员为类自身的指针或引用时,采用前置声明; |
可以依赖声明,就不要依赖定义; |
|
内联函数 |
1. 不要内联超过10行的函数; 2. 析构函数应慎重对待; 3. 内联包含循环或switch语言的函数将得不偿失; 4. 虚函数和递归函数即使被声明为内联也不一定是内联函数; |
|
|
-inl.h函数 |
1. 复杂的内联函数定义,放在后缀名为-inl.h的头文件中; |
|
|
函数参数顺序 |
1. 函数参数顺序:输入参数在前,输出参数在后; |
1. 输入参数为传值或常数引用、常指针; 2. 输出参数为非常数指针、非常数引用; |
|
包含文件的名字和顺序 |
1. 包含.h文件次序:优先的.h文件、C库、C++库、其他库的.h、项目内的.h; 2. 项目内.h文件应按照项目源代码目录树结构排列,并且避免使用UNIX目录.(当前目录)和..(父目录); 3. 相同目录下.h文件按字母序排列; |
1. google-awe-project/src/base/logging.h应这样被包含: #include “base/logging.h”; 2. 某foo.cc(或foo.cpp)文件中包含.h文件次序: 1 #include “foo/public/foo.h” // 优先.cc或.cpp对应的头文件 2 #include <sys/types.h> // C库 3 #include <hash_map> // C++库 4 #include <foo/public/bar.h> // 项目内头文件 |
|
作用域 |
作用域 |
1. 在.cpp文件(不能在.h文件)中,使用不具名的命名空间; 2. 具名命名空间的名称基于项目或路径名称; 3. 不要使用using指示符,避免污染命名空间,可以使用using; 4. 在.cpp文件、.h文件中的函数和类中,可以使用using; 5. 在.cpp文件、.h文件中的函数和类中,可以使用命名空间别名; 6. 不要使用inline namespace; |
1. 在.cpp文件中的不具名命名空间: 1 namespace 2 { 3 enum {UNUSED, EOF, ERROR}; // 无缩进 4 bool foo() {return EOF;} 5 } // namespace 2. 具名命名空间: 1 namespace mynamespace 2 { 3 class MyClass 4 { 5 public: 6 void foo(); 7 }; 8 } // namespace mynamespace |
嵌套类 |
1. 嵌套类不作为接口使用时,不要定义为public; 2. 嵌套类作为接口的一部分时,置于命名空间中; |
非接口嵌套类: 1 class Foo 2 { 3 private: 4 class Bar 5 { 6 }; 7 }; |
|
非成员函数、静态成员函数、全局函数 |
1. 使用命名空间中的非成员函数或静态成员函数,尽量不使用全局函数; 2. 若确定需要定义非成员函数,并且只在.cpp文件中使用它,可使用不具名命名空间或static关联限定其作用域; |
|
|
局部变量 |
1. 将函数变量尽可能置于最小作用域内,在声明变量时将其初始化; |
|
|
静态和全局变量 |
1. 禁止class类型的全局(静态)变量,若一定要使用,可单例模式; 2. 多线程代码中禁止非常数全局变量,不可使用函数返回值初始化全局变量; 3. 全局字符串常量,使用C风格字符串,而不要使用STL字符串; 4. 大多数全局变量应该是类的静态数据成员,或当其只在.cpp文件中使用时,将其定义到不具名命名空间中,或者使用静态关联以限制变量的作用域; 5. 静态成员变量视作全局变量,不能是class类型; |
C风格字符串常量: 1 const char kFrogSays[] = “ribbet”; |
|
类 |
构造函数职责 |
1. 构造函数只进行那些没有实际意义的初始化; 2. 如果对象需要有意义的初始化,可以采用工厂函数或者Init()方法集中初始化; 3. 构造函数禁止调用虚函数; |
|
初始化 |
1. 若类中定义了成员变量,没有提供其他构造函数,需要定义一个默认构造函数; |
|
|
显式构造函数 |
1. 除非必要,单参数构造函数使用C++关键字explicit; |
1 explicit Foo(string name); |
|
拷贝构造函数 |
1. 仅在代码中需要拷贝一个类对象的时候使用拷贝构造函数,不需要拷贝时应使用DISALLOW_COPY_AND_ASSIGN; |
可以考虑在类的private中添加空的拷贝构造函数和赋值操作,并且只有声明,不进行定义; 1 #define DISALLOW_COPY_AND_ASSIGN(Type) \ 2 Type(const Type&); \ 3 void operator = (const Type&) 4 5 class Foo 6 { 7 public: 8 Foo(int f); 9 ~Foo(); 10 private: 11 DISALLOW_COPY_AND_ASSIGN(Foo); 12 }; |
|
结构体和类 |
1. 仅当只有数据时使用struct,其它情况使用class; 2. 对于functor和trait,可以使用struct; 3. 类和结构体的成员变量使用不同的命名规则(见下面的命名约定); |
|
|
继承 |
1. 所有继承必须为public继承,采用基类对象作为成员的方式替代私有继承; 2. 不要过多使用实现继承,更多使用组合; 3. 使析构函数为virtual,如果该类具有虚函数,其析构函数一定为虚函数; 4. 限定仅在子类访问的成员函数为protected,数据成员应始终为私有; 5. 重定义派生的虚函数时,在派生类中明确声明其为virtual; |
|
|
多继承 |
1. 只有当最多一个基类中含有实现,其他基类都是以Interface为后缀的纯接口类时才使用多继承; 2. 纯接口必须以Interface为后缀; |
|
|
接口 |
1. 满足纯接口要求时,类以Interface结尾; 2. 接口类必须声明虚析构函数,析构函数不能纯虚函数; |
满足纯接口类的要求: * 只有纯虚函数和静态函数(析构函数除外); * 没有非静态数据成员; * 没有定义任何构造函数,若有,须不含参数,且为protected; * 如果是子类,只能继承满足以上条件并以Interface为后缀的类; |
|
操作符重载 |
1. 一般不要重载操作符,如果需要的话,可以定义类似Equal()、CopyFrom()等函数; 2. STL容器中作为key要重载operator==或operator<,可以在声明容器的时候,创建相等判断和大小比较的仿函数类型; |
|
|
访问控制 |
1. 将类数据成员设为private,并提供相关存取函数; 2. 存取函数的定义一般内联在头文件中; |
定义变量m_foo及其取值函数foo()、赋值函数setFoo(); |
|
声明顺序 |
1. 类中定义次序:public:、protected:、private:; 2. 每一块中,声明次序为:typedef和enum、常量(static const数据成员)、构造函数、析构函数、成员函数(含静态成员函数)、数据成员(除static const数据成员); 3. 宏DISALLOW_COPY_AND_ASSIGN置于private:块之后,作为类的最后部分; 4. .cc文件中函数的定义顺序和声明次序一致; |
|
|
编写短小函数 |
1. 如果函数超过40行,可以考虑在不影响程序结构的情况下将其分割; |
|
|
其它C++特性 |
智能指针 |
1. 任何情况下禁止使用auto_ptr; |
|
引用参数 |
1. 所有按引用传递的参数必须为const引用; 2. 输入参数采用const引用,输出参数采用指针; 3. 输入参数可以是const指针,但不可以是non-const引用; |
|
|
函数重载 |
1. 仅在输入参数类型不同、功能相同时使用重载函数; |
|
|
缺省参数 |
1. 禁止使用缺省参数; |
|
|
变长数组和alloca |
1. 禁止使用变长数组和alloca(); |
|
|
友元 |
1. 可以合理使用友元类及友元函数; |
|
|
异常 |
1. 禁止使用异常; |
|
|
运行时类型识别 |
1. 除单元测试外,禁止使用RTTI; |
|
|
类型转换 |
1. 使用C++风格而不要使用C风格类型转换; 2. 除单元测试外不要使用dynamic_cast; |
使用static_case <> ()等C++的类型转换; |
|
流 |
1. 除日志接口外,使用printf之类的代替流; |
最好选择printf + read/write; |
|
前置自增和自减 |
1. 对于迭代器和其他模板对象使用前缀形式自增和自减运算符; |
|
|
const使用 |
1. 在任何可以使用的情况下使用const; |
|
|
整型 |
1. 使用断言声明变量为非负数,不要使用无符号型; |
|
|
64位下的可移植性 |
1. printf指定的一些类型在32位和64位系统上可移植性不是很好; 2. sizeof(void *) != sizeof(int),可以用intptr_t定义指针大小的整数; 3. 结构体字节对齐; 4. 创建64位常量时使用LL或ULL作为后缀; |
int64_t my_value = 0x123456LL; |
|
预处理宏 |
1. 慎用宏,尽可能以内联函数、枚举和常量代替; 2. 在.h文件中,除了#define防止头文件重包含外,不要定义宏; |
|
|
0和NULL |
1. 整数用0,实数用0.0,指针用NULL,字符(串)用’\0’; |
|
|
sizeof |
1. 尽可能用sizeof(varname)代替sizeof(type); |
|
|
Boost库 |
1. 只使用Boost中被认可的库: Compressed Pair:boost/compressed_pair.hpp; Pointer Container:boost/ptr_container,其中不包括ptr_array.hpp和serialization; |
|
|
命名约定 最重要是一致性 |
通用命名规则 |
1. 函数、变量、文件命名应具有描述性,不要过度缩写,类型和变量应是名词,函数名可以用“命令性”动词; 2. 除非放到项目外也非常明了,否则不要使用缩写; |
1.int num_completed_connections; 2.int num_dns_connections; 3.int error_count; // Good. int error_cnt; // Bad. |
文件命名 |
1. 文件名要全部小写,可以包含’_’或’-’,按项目约定来; 2. 源文件以.cc结尾,头文件以.h结尾;
|
可接受的文件命名: my_useful_class.cc my-useful-class.cc myusefulclass.cc |
|
类型命名 |
1. 类型命名每个单词以大写字母开头,不包含下划线; |
1. 类型:类、结构体、类型定义(typedef)、枚举; 2. 如:MyExcitingClass、UrlTable; |
|
变量命名 |
1. 变量名一律小写,单词间以下划线相连,类的成员变量以下划线结尾; 2. 结构体数据成员可以和普通变量一样,不用像类的成员数据那样以下划线结尾; 3. 全局变量以g_作为前缀; |
1. 1 my_exciting_local_variable 2 my_exciting_member_variable_ 2. 1 struct UrlTablePoperties 2 { 3 sting name; 4 int num_entries; 5 }; |
|
常量命名 |
1. 在名称前加k; |
const int kDayInAWeek = 7; |
|
函数命名 |
1. 普通函数为大小写混合,存取函数则需要与变量名匹配; 2. 其它短小的内联函数可以使用小写字母; |
1 MyExcitingMethod() 2 set_my_exciting_member_variable() |
|
命名空间 |
1. 命名空间全部为小写; |
|
|
枚举命名 |
1. 枚举值全部大写,单词间以下划线相连; |
|
|
宏命名 |
1. 如果要使用,其命名方式与枚举命名一致; |
|
|
代码注释 |
注释风格 |
1. 使用//或/* */,统一就好; |
|
文件注释 |
1. 在每一个文件开头加入版权公告,然后是文件内容描述; |
|
|
类注释 |
1. 类的定义要附着描述类的功能和用法的注释; 2. 如果类的实例可被多线程访问,使用时务必文档说明; |
|
|
函数注释 |
1. 函数声明处注释描述函数功能,定义处描述函数实现; 2. 构造/析构函数前无需注释; |
1. 函数声明处注释的内容: * inputs及outputs; * 类成员函数:函数调用期间对象是否需要保持引用参数,是否会释放这些参数; * 如果函数分配了空间,需要由调用者释放; * 参数是否可以为NULL; * 是否存在函数使用的性能隐忧; * 如果函数是可重入的,其同步前提是什么; 2. 函数定义处定义的内容: 注释说明函数功能和实现要点; |
|
变量注释 |
1. 通常变量名本身足以很好说明变量用途,特定情况下,需要额外注释说明; |
|
|
实现注释 |
1. 对于实现代码中巧妙的、晦涩的、有趣的、重要的地方加以注释; 2. 相邻几行都有注释的,可以调整使//纵向对齐; |
|
|
TODO注释 |
1. 对那些临时的、短期的解决方案,或已经够好但并不完美的代码使用TODO注释; |
// TODO(hanyp@126.com): change this. |
|
格式 |
行长度 |
1. 每一行代码字符数不超过80; |
|
空格 |
1. 只使用空格,每次缩进2个字符。设定编译器将Tab转为空格; |
|
|
函数声明和定义 |
1. 函数名、返回类型、参数尽可能在同一行; 2. 如果函数为const的,const应与最后一个参数位于同一行; 3. 独立行的参数保持4个空格的缩进; |
1 ReturnType ClassName::FunctionName(Type ar_name1, Type par_name2) 2 { 3 DoSomething(); 4 } |
|
函数调用 |
1. 同函数声明和定义格式; |
|
|
条件语句 |
1. 不要再圆括号内加空格; |
1 if (condition) 2 { 3 ... 4 } 5 else 6 { 7 ... 8 } |
|
循环和选择语句 |
1. 类比条件语句; |
|
|
指针和引用表达式 |
1. 句点(.)或箭头(->)前后不要有空格,指针/地址操作符(*、&)后不要空格; 1. 在声明指针、引用变量或参数时,(*、&)与类型名紧挨; |
|
|
布尔表达式 |
1. 如果一个布尔表达式超过标准行宽,断行要统一; |
|
|
函数返回值 |
1. return表达式中不要使用圆括号; |
|
|
变量及数组初始化 |
1. 使用()格式; |
int x(3); |
|
预处理指令 |
1. 预处理指令不需要缩进; |
|
|
类格式 |
1. public、protected、private不需要缩进,函数及变量定义缩进2空格; |
|
|
初始化列表 |
1. 构造函数初始化列表放在同一行或按四格缩进并排几行; 2. ‘:’前后各空一格; |
|
|
命名空间格式 |
1. 命名空间内容不需要缩进,命名空间不添加额外缩进层次; |
|
本页共1038段,38209个字符,46264 Byte(字节)